﻿/**
* @description  Abstract base class for all graphic transitions.
*/
package com.foed.animation.transitions {
	
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;

	import com.foed.animation.Animator;
	import com.foed.animation.easing.*;
	import com.foed.animation.TweenRunner;
	import com.foed.events.TweenEvent;
	
	public class Transition extends Animator {
		
		protected var _clipHolder:Sprite;

		private var _tweenRunner:TweenRunner;
		private var _time:Number;
		private var _clip:DisplayObject;
		private var _easeFunction:Function;
		private var _type:String;
		private var _startTransform:Object;
		private var _endTransform:Object;

		/**
		* @description  Constructor for abstract class. This should not be called directly, only through concrete classes.
		*
		* @param  pClip  The clip on which to perform the transition.
		* @param  pStartTransform  The starting values for the color transition.
		* @param  pEndTransform  The destination values for the color transition.
		* @param  pTime  The length in milliseconds for the transition.
		* @param  pType  The type of transition. IN moves from startTransform to endTransfom. OUT does the opposite.
		* @param  pEaseFunction  The function to use to transform the transition values over time.
		*/
		public function Transition(
			pClip:DisplayObject=null,
			pTime:Number=1000,
			pStartTransform:Object=null,
			pEndTransform:Object=null,
			pType:String=null,
			pEaseFunction:Function=null
		) {
			_clip = pClip;
			_time = pTime;
			_easeFunction = pEaseFunction || Linear.easeNone;
			_type = pType || TransitionType.IN;
			startTransform = pStartTransform;
			endTransform = pEndTransform;
		}
	
		/**
		* @description  Handler for the event fired at the end of the tween.
		*
		* @param  pEvent  The event dispatched by the TweenRunner.
		*/
		private function onEndTween(pEvent:TweenEvent):void {
			cleanUp();
			dispatchEvent(new TweenEvent(TweenEvent.END, this));
		}

		/**
		* @description  Event handler for TweenRunner's animation interval.
		*
		* @param  pEvent  Event fired by TweenRunner.
		*/
		protected function onChangeTween(pEvent:TweenEvent):void {
			runTransition(_tweenRunner.value);
			dispatchEvent(new TweenEvent(TweenEvent.CHANGE, this));
		}

		/**
		* @description  Called each iteration of the tween to transform values.
		*				This needs to be overridden by concrete classes.
		*
		* @param  pChangeValues  The current values of the transition at each iteration of the tween.
		*/
		protected function runTransition(pChangeValues:Object):void {}

		/**
		* @description  Prepares clip to have transitions applied by adding new parent clip.
		*/
		protected function prepare():void {
			_clipHolder = new Sprite();
			_clip.visible = true;
			var pParent:DisplayObjectContainer = _clip.parent;
			var pDepth:int = pParent.getChildIndex(_clip);
			pParent.removeChild(_clip);
			pParent.addChildAt(_clipHolder, pDepth);
			_clipHolder.addChild(_clip);
		}
		
		/**
		* @description  Removes any artifacts of the transition.
		*/
		protected function cleanUp():void {
			if (_clipHolder == null || _clip.parent != _clipHolder) return;
			var pParent:DisplayObjectContainer = _clipHolder.parent;
			var pDepth:int = pParent.getChildIndex(_clipHolder);
			pParent.removeChild(_clipHolder);
			pParent.addChildAt(_clip, pDepth);
			_clipHolder = null;
			_clip.visible = true;
		}
	
		/**
		* @description  Begins the transition.
		*/
		override public function start():void {
			prepare();
			if (_type == TransitionType.IN) {
				_tweenRunner = new TweenRunner(_startTransform, _endTransform, _time, _easeFunction);
			} else {
				_tweenRunner = new TweenRunner(_endTransform, _startTransform, _time, _easeFunction);
			}
			_tweenRunner.addEventListener(TweenEvent.CHANGE, onChangeTween);
			_tweenRunner.addEventListener(TweenEvent.END, onEndTween);
			dispatchEvent(new TweenEvent(TweenEvent.START, this));
			_tweenRunner.start();
		}
	
		/**
		* @description  Stops the transition.
		*/
		override public function halt():void {
			if (_tweenRunner != null) {
				_tweenRunner.removeEventListener(TweenEvent.CHANGE, onChangeTween);
				_tweenRunner.removeEventListener(TweenEvent.END, onEndTween);
				_tweenRunner.halt();
				_tweenRunner = null;
			}
			if (clip != null)
				runTransition((_type == TransitionType.IN) ? _endTransform : _startTransform);
			cleanUp();
		}
	
		/**
		* @description  Cleans up any timers that might be running.
		*/
		override public function die():void {
			super.die();
			cleanUp();
			_clip = null;
			_startTransform = null;
			_endTransform = null;
			_easeFunction = null;
		}
		
		/**
		* @description  Returns the length in milliseconds for the transition.
		*
		* @returns  The length in milliseconds for the transition.
		*/
		public function get time():Number {
			return _time;
		}
	
		/**
		* @description  Sets the length in milliseconds for the transition.
		*
		* @param  pTime  The length in milliseconds for the transition.
		*/
		public function set time(pTime:Number):void {
			_time = pTime;
		}
	
		/**
		* @description  Returns the reference to the display object being transitioned.
		*
		* @returns  The DisplayObject instance being transitioned.
		*/
		public function get clip():DisplayObject {
			return _clip;
		}
	
		/**
		* @description  Sets the display object being transitioned.
		*
		* @param  pClip  The DisplayObject instance being transitioned.
		*/
		public function set clip(pClip:DisplayObject):void {
			_clip = pClip;
		}
	
		/**
		* @description  Returns the easing equation that will be used for transforming the transition values over time.
		*
		* @returns  The easing equation that will be used for transforming the transition values over time.
		*/
		public function get easeFunction():Function {
			return _easeFunction;
		}
	
		/**
		* @description  Sets the easing equation that will be used for transforming the transition values over time.
		*
		* @param  pFunction  The easing equation that will be used for transforming the transition values over time.
		*/
		public function set easeFunction(pFunction:Function):void {
			_easeFunction = pFunction;
		}
	
		/**
		* @description  Returns the type of transition. This should be a constant of TransitionType.
		*
		* @returns  The type of transition.
		*/
		public function get type():String {
			return _type;
		}
	
		/**
		* @description  Sets the type of transition. This should be a constant of TransitionType.
		*
		* @param  pType  The type of transition.
		*/
		public function set type(pType:String):void {
			_type = pType;
		}

		/**
		* @description  Returns the starting value of the property or the starting values for the collection of properties
		*				that will be transformed during the transition.
		*
		* @returns  The starting value for the transition.
		*/
		public function get startTransform():Object {
			return _startTransform;
		}
	
		/**
		* @description  Sets the starting value of the property or the starting values for the collection of properties
		*				that will be transformed during the transition.
		*
		* @param  pObject  The starting value for the transition.
		*/
		public function set startTransform(pObject:Object):void {
			_startTransform = pObject;
		}
	
		/**
		* @description  Returns the destination value of the property or the destination values for the collection of properties
		*				that will be transformed during the transition.
		*
		* @returns  The destination value for the transition.
		*/
		public function get endTransform():Object {
			return _endTransform;
		}
	
		/**
		* @description  Sets the destination value of the property or the destination values for the collection of properties
		*				that will be transformed during the transition.
		*
		* @param  pObject  The destination value for the transition.
		*/
		public function set endTransform(pObject:Object):void {
			_endTransform = pObject;
		}
	
	}

}